define([], function() {
    var locFactory = angular.module('app.factory.location', []);

    function Location(url) {
        const urlReg = /^(?:([A-Za-z]+):)?\/\/([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
        const searchReg = /([\?|&])(.+?)=([^&?]*)/g;
        const urlRegNames = ['url', 'protocol', 'host', 'port', 'path', 'search', 'hash'];

        this.attributes = (function(url) {
            var attrs = {};
            var matches = urlReg.exec(decodeURI(url));
            for (var i = 0; i < urlRegNames.length; i++) {
                if (urlRegNames[i] == 'search') {
                    attrs[urlRegNames[i]] = {};
                    if (matches[i]) {
                        var query = '?' + matches[i];
                        var params = searchReg.exec(query);
                        while (params) {
                            attrs[urlRegNames[i]][params[2]] = params[3];
                            params = searchReg.exec(query);
                        }
                    }
                } else {
                    attrs[urlRegNames[i]] = matches[i];
                }
            }
            return attrs;
        })(url);

        (function (_this) {
            for (var i = 0; i < urlRegNames.length; i++) {
                (function(attrName) {
                    if (attrName == 'url') {
                        _this[attrName] = function() {
                            var url = _this.attributes['protocol'] + '://' + _this.attributes['host'];
                            if (_this.attributes['port']) {
                                url += ':' + _this.attributes['port'];
                            }
                            if (_this.attributes['path']) {
                                url += '/' + _this.attributes['path'];
                            }
                            if (_this.attributes['search']) {
                                var params = [];
                                for (name in _this.attributes['search']) {
                                    params.push(name + '=' + _this.attributes['search'][name]);
                                }
                                if (params.length > 0) {
                                    url += '?' + params.join('&');
                                }
                            }
                            if (_this.attributes['hash']) {
                                url += '#' + _this.attributes['hash'];
                            }
                            return encodeURI(url);
                        }
                    } else if (attrName == 'search') {
                        _this[attrName] = function(name, value) {
                            if (name == undefined) {
                                return _this.attributes[attrName];
                            } else if (name.hasOwnProperty) {
                                // use name as value
                                _this.attributes[attrName] = name;
                            } else {
                                if (value != undefined) {
                                    if (value == null) {
                                        delete _this.attributes[attrName][name];
                                    } else {
                                        _this.attributes[attrName][name] = value;
                                    }
                                } else {
                                    return _this.attributes[attrName][name];
                                }
                            }
                        }
                    } else {
                        _this[attrName] = function(value) {
                            if (value != undefined) {
                                _this.attributes[attrName] = value;
                            } else {
                                return _this.attributes[attrName];
                            }
                        }
                    }
                })(urlRegNames[i]);
            }
        })(this);
    }

    locFactory.factory('xLocation', function() {
        return {
            parse: function(url) {
                return new Location(url);
            }
        }
    });

    return locFactory;
});
